package org.zhuzx.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;

/**
 * JDBC工具类
 * @author	zhuzx	2016年10月10日
 */
public class JdbcUtil {
	
	private static Connection conn;
	
	/**
	 * 读取配置文件生成一个数据源连接
	 * @param confFileName 数据库配置properties文件，可指定路径（配置文件不在classpath下的情况）。
	 */
	public static Connection getDbConn(String confFileName) {
		if (conn != null) {
			try {
				if (!conn.isClosed()) {
					return conn;
				}
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
		}
		//若是外部调用者，则肯定会传入调用者所在项目的配置，若未传入，则是在sundry项目中运行的。
		if (GeneralUtil.isBlank(confFileName)) {
			confFileName = "zhuzx/shared/db.properties";
		}
		Properties conf = new Properties();
		InputStream is = GeneralUtil.loadFileFromClasspath(confFileName);
		try {
			conf.load(new InputStreamReader(is, StandardCharsets.UTF_8));
			is.close();
			String driverClass = conf.get("driverClass").toString();
			if (driverClass.contains("oracle")) {
				//oracle需要这个参数来获取注释
				conf.put("remarksReporting", "true");
			}
			System.setProperty("jdbc.drivers", driverClass);
			Connection conn = DriverManager.getConnection(conf.get("jdbcUrl").toString(), conf);
			JdbcUtil.conn = conn;
			return conn;
		} catch (IOException | SQLException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 生成一个数据库连接
	 */
	public static Connection getDbConn(String url, String user, String password) {
		if (GeneralUtil.isBlank(System.getProperty("jdbc.drivers"))) {
			String driver = "sun.jdbc.odbc.JdbcOdbcDriver";
			if (url.contains("mysql")) {
				driver = "com.mysql.cj.jdbc.Driver";
			} else if (url.contains("oracle")) {
				driver = "oracle.jdbc.driver.OracleDriver";
			}
			System.setProperty("jdbc.drivers", driver);
		}
		try {
			return DriverManager.getConnection(url, user, password);
		} catch (SQLException e) {
			throw new IllegalArgumentException(e);
		}
	}

	/**
	 * 关闭这个数据库连接
	 */
	public static void close(Connection conn) {
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
		}
	}
	
	/**
	 * 执行查询语句，将返回的结果集封装为一个由map组成的列表。<br>
	 * 不会关闭传入的Connection。
	 */
	public static List<Map<String,String>> executeQuery(String sql, Connection conn) {
		try {
			Statement stm = conn.createStatement();
			ResultSet rs = stm.executeQuery(sql);
			ResultSetMetaData rsmd = rs.getMetaData();
			int columnCount = rsmd.getColumnCount();
			String[] columnName = new String[columnCount];
			String[] columnType = new String[columnCount];
			for (int i=0; i<columnCount; i++) {
				columnName[i] = rsmd.getColumnLabel(i+1);
				columnType[i] = rsmd.getColumnClassName(i+1);
			}
			List<Map<String,String>> list = new ArrayList<>();
			while (rs.next()) {
				Map<String,String> row = new LinkedHashMap<>(columnCount);
				for (int i=0; i<columnCount; i++) {
					String value = rs.getString(columnName[i]);
					//特殊处理：即使精度只到秒，Timestamp的toString方法也会缀加.0。
					if (!GeneralUtil.isBlank(value) && columnType[i].equals("java.sql.Timestamp")) {
						if (value.endsWith(".0")) {
							value = value.substring(0, 19);
						}
					}
					//这个getString方法，对于值很大的浮点数字段，会不会得到科学记数法格式的文本？可能取决于数据库驱动实现。
					row.put(columnName[i], value);
				}
				list.add(row);
			}
			stm.close();
			return list;
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 执行查询语句，将返回的结果集封装为一个由map组成的列表。<br>
	 * 自动获取连接并关闭。
	 */
	public static List<Map<String,String>> executeQuery(String sql) {
		Connection conn = getDbConn(null);
		List<Map<String,String>> list = executeQuery(sql, conn);
		close(conn);
		return list;
	}
	
	/**
	 * 执行查询语句，将返回的结果集封装为一个由map组成的列表。<br>
	 * 不会关闭传入的Connection。
	 */
	public static List<Map<String,Object>> executeQueryForObject(String sql, Connection conn) {
		try {
			Statement stm = conn.createStatement();
			ResultSet rs = stm.executeQuery(sql);
			ResultSetMetaData rsmd = rs.getMetaData();
			int columnCount = rsmd.getColumnCount();
			String[] columnName = new String[columnCount];
			for (int i=0; i<columnCount; i++) {
				columnName[i] = rsmd.getColumnLabel(i+1);
			}
			List<Map<String,Object>> list = new ArrayList<>();
			while (rs.next()) {
				Map<String,Object> row = new LinkedHashMap<>(columnCount);
				for (int i=0; i<columnCount; i++) {
					row.put(columnName[i], rs.getObject(columnName[i]));
				}
				list.add(row);
			}
			stm.close();
			return list;
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 执行查询语句，将返回的结果集封装为一个由map组成的列表。<br>
	 * 自动获取连接并关闭。
	 */
	public static List<Map<String,Object>> executeQueryForObject(String sql) {
		Connection conn = getDbConn(null);
		List<Map<String,Object>> list = executeQueryForObject(sql, conn);
		close(conn);
		return list;
	}
	
	/**
	 * 执行只返回单个值的语句
	 */
	public static String executeQueryForSingleValue(String sql) {
		List<Map<String,String>> list = executeQuery(sql);
		if (list.size() != 1 || list.get(0).size() != 1) {
			throw new IllegalArgumentException("不是返回单个值：\n" + sql);
		}
		for (String value : list.get(0).values()) {
			return value;
		}
		return null;
	}
	
	/**
	 * 执行更新语句（包括insert、update），返回受影响的行数。<br>
	 * 不会关闭传入的Connection，但可能会改变它的autoCommit属性。
	 */
	public static int executeUpdate(String sql,
			Connection conn, boolean isInTransaction) {
		try {
			boolean isOriginAutoCommit = conn.getAutoCommit();
			if (isInTransaction && isOriginAutoCommit) {
				conn.setAutoCommit(false);
			}
			Statement stm = conn.createStatement();
			int rowCount = stm.executeUpdate(sql);
			stm.close();
			if (!isInTransaction) {
				conn.commit();
				//如果在事务期间更改了自动提交模式，则会提交事务。
				if (isOriginAutoCommit) {
					conn.setAutoCommit(true);
				}
			}
			return rowCount;
		} catch (SQLException e) {
			try {
				conn.rollback();
			} catch (SQLException e1) {
				throw new RuntimeException(e1);
			}
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 执行更新语句（包括insert、update），返回受影响的行数。<br>
	 * 自动获取连接并关闭。
	 */
	public static int executeUpdate(String sql) {
		Connection conn = getDbConn(null);
		int rowCount = executeUpdate(sql, conn, false);
		close(conn);
		return rowCount;
	}
	
	/**
	 * 为sql添加where子句和limit条件，以及生成count语句，返回两条完整的sql。
	 * @param sql 前半段语句，包括select、from以及基本where条件。
	 * @param params 动态构建where条件的参数值
	 * @param whereArray 需要添加的条件，每个子数组需要三个元素：
	 * 						传入的参数名，sql中匹配的字段及操作符如t2.create_time >= '，参数值收尾符如 00:00:00'。
	 * @param suffix 尾部的其它子句，如group by和order by。
	 * @return 两条sql组成的数组：分页查询语句，计算总数的count语句。
	 */
	public static <V> String[] appendWhereAndCount(String sql, Map<String,V> params,
			String[][] whereArray, String suffix) {
		StringBuilder where = new StringBuilder();
		for (String[] pair : whereArray) {
			V value = params.get(pair[0]);
			if (!GeneralUtil.isBlank(value)) {
				where.append(" and ").append(pair[1]).append(value).append(pair[2]);
			}
		}
		sql += where + " " + suffix;
		String countSql = "select count(*) from (" + sql + ") t0";
		int page = Integer.parseInt(params.get("page").toString());
		int rows = Integer.parseInt(params.get("rows").toString());
		sql += " limit " + (page-1)*rows + "," + rows;
		return new String[] {sql, countSql};
	}
	
	/**
	 * 构建预备插入语句和所需的配套参数值数组
	 * @param sql 传入此参数以指定表名，方法内部会append出完整的预备语句。
	 * @param list 已处理好的ListMap，与想要插入的数据库表字段精确对应，不可有多余的键。
	 * @param uniqFields 若需要以on duplicate方式处理，则将唯一索引字段列出。
	 * @return 预备语句需要的值参数组成的数组，其中每个子数组对应一次语句执行所需全部参数值。
	 */
	public static <V> Object[][] prepareInsertData(StringBuilder sql, List<Map<String,V>> list,
			String... uniqFields) {
		if (!sql.toString().trim().substring(0,6).equalsIgnoreCase("insert")) {
			sql.insert(0, "insert into ");
		}
		if (sql.lastIndexOf("(") == -1) {
			sql.append(" (");
		}
		Object[][] values = new Object[list.size()][];
		StringBuilder placeHolder = new StringBuilder();
		for (Map.Entry<String,V> entry : list.get(0).entrySet()) {
			sql.append(entry.getKey()).append(",");
			placeHolder.append("?,");
		}
		sql.deleteCharAt(sql.length()-1);
		sql.append(") values (");
		placeHolder.deleteCharAt(placeHolder.length()-1);
		placeHolder.append(")");
		sql.append(placeHolder);
		int fieldNum = list.get(0).size();
		for (int i=0; i<list.size(); i++) {
			Map<String,V> row = list.get(i);
			Object[] value = new Object[fieldNum];
			int j = 0;
			for (Map.Entry<String,V> entry : row.entrySet()) {
				value[j] = entry.getValue();
				j++;
			}
			values[i] = value;
		}
		if (!GeneralUtil.isEmpty(uniqFields)) {
			addOnDuplicatePart(sql, list, values, uniqFields);
		}
		return values;
	}
	
	/**
	 * 缀加on duplicate子句部分
	 * @param sql 已完备的普通insert语句
	 * @param list 与构建insert语句时一致
	 * @param values 构建insert语句后得到的数组，本方法会把其中每个子数组替换成合并后的版本。
	 * @param uniqFields on duplicate语句所依赖的唯一性字段（联合唯一索引需要列举全部联合字段）
	 */
	private static <V> void addOnDuplicatePart(StringBuilder sql, List<Map<String,V>> list,
			Object[][] values, String... uniqFields) {
		Set<String> set = new HashSet<>(list.get(0).keySet());
		if (!GeneralUtil.isEmpty(uniqFields)) {
			for (String field : uniqFields) {
				set.remove(field);
			}
		}
		List<String> fields = new ArrayList<>(set);
		sql.append(" on duplicate key update ");
		for (String field : fields) {
			sql.append(field).append("=?,");
		}
		sql.deleteCharAt(sql.length() - 1);
		for (int i=0; i<values.length; i++) {
			Map<String,V> row = list.get(i);
			Object[] updateValue = new Object[fields.size()];
			for (int j=0; j<updateValue.length; j++) {
				updateValue[j] = row.get(fields.get(j));
			}
			values[i] = GeneralUtil.getCombinedArray(values[i], updateValue);
		}
	}
	
	/**
	 * 给PreparedStatement语句设置值
	 */
	public static void setRowValue(PreparedStatement pstmt, Object[] value) {
		try {
			for (int j=0; j<value.length; j++) {
				Object o = value[j];
				if (o != null && o.toString().length() == 0) {
					o = null;
				}
				pstmt.setObject(j+1, o);
			}
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 使用预备语句批量插入数据
	 * @param tableName 插入的表名
	 * @param list 已处理好的ListMap，与想要插入的数据库表字段精确对应，不可有多余的键。
	 * @param uniqFields 若需要以on duplicate方式处理，则将唯一索引字段列出。
	 */
	public static <V> void insertByBatch(Connection conn,
			String tableName, List<Map<String,V>> list, String... uniqFields) {
		StringBuilder sql = new StringBuilder(tableName);
		Object[][] values = prepareInsertData(sql, list, uniqFields);
		try {
			boolean autoCommit = conn.getAutoCommit();
			conn.setAutoCommit(false);
			PreparedStatement pstmt = conn.prepareStatement(sql.toString());
			for (int i=0; i<values.length; i++) {
				Object[] row = values[i];
				setRowValue(pstmt, row);
				pstmt.addBatch();
				if (i > 0 && i % 1000==0) {
					pstmt.executeBatch();
				}
			}
			pstmt.executeBatch();
			pstmt.close();
			conn.commit();
			conn.setAutoCommit(autoCommit);
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static <V> String createBatchInsertSql(String tableName, List<Map<String,V>> list) {
		if (list.size() == 0) {
			return "";
		}
		Set<String> fieldNames = new LinkedHashSet<>();
		list.forEach(map -> fieldNames.addAll(map.keySet()));
		StringBuilder sql = new StringBuilder("insert into " + tableName + " (");
		fieldNames.forEach(field -> sql.append(field).append(","));
		sql.deleteCharAt(sql.length() - 1).append(") values\n('");
		for (Map<String,V> map : list) {
			fieldNames.forEach(field -> sql.append(escapeSqlParamValue(map.get(field))).append("','"));
			sql.delete(sql.length()-2, sql.length());
			sql.append("),\n('");
		}
		sql.delete(sql.length()-4, sql.length());
		String result = sql.toString().replace("''", "null");
		result = result.replace("'null'", "null");
		return result;
	}

	private static String escapeSqlParamValue(Object value) {
		return MicroUtil.escapeSqlParamValue(GeneralUtil.getNotBlankString(value, null));
	}
	
	/**
	 * 生成可直接执行的单条插入语句
	 * @param tableName 需要插入的表名
	 * @param params 各个字段名及对应字段值，与想要插入的数据库表字段精确对应，不可有多余的键。
	 * @param uniqFields 若需要以on duplicate方式处理，则将唯一索引字段列出。
	 */
	public static <V> String createInsertSql(String tableName, Map<String,V> params,
			String... uniqFields) {
		StringBuilder fields = new StringBuilder("insert into " + tableName + " (");
		StringBuilder values = new StringBuilder("\n\t\tvalues ('");
		for (Map.Entry<String,V> entry : params.entrySet()) {
			fields.append(entry.getKey()).append(",");
			values.append(escapeSqlParamValue(entry.getValue())).append("','");
		}
		values.delete(values.length()-2, values.length());
		String valuesStr = values.toString().replace("''", "null");
		valuesStr = valuesStr.replace("'null'", "null");
		fields.deleteCharAt(fields.length() - 1);
		String sql = fields + ")" + valuesStr + ")";
		if (!GeneralUtil.isEmpty(uniqFields)) {
			Map<String,V> map = new HashMap<>(params);
			for (String uniqField : uniqFields) {
				map.remove(uniqField);
			}
			sql += " on duplicate key update " + createUpdateSqlPart(map);
		}
		return sql;
	}

	public static <V> String createBatchInsertSqlForOracle(String tableName, List<Map<String,V>> list) {
		if (list.size() == 0) {
			return "";
		}
		StringBuilder sb = new StringBuilder("insert all\n");
		for (Map<String,V> map : list) {
			sb.append(createInsertSql(tableName, map).substring(7)).append("\n");
		}
		sb.deleteCharAt(sb.length() - 1).append(";\n");
		sb.append("select 1 from dual");
		return sb.toString();
	}
	
	/**
	 * 生成可直接执行的脚本，带提示、屏蔽变量、提交事务等等。可以和别的脚本段直接拼接，不需要以分号或者换行符分隔。
	 */
	public static <V> String createBatchInsertScriptForOracle(
			String tableName, List<Map<String,V>> list, String promptOfCompletion) {
		String sql = createBatchInsertSqlForOracle(tableName, list);
		String prefix = "prompt Importing into table " + tableName +
				" for " + list.size() + " records...\n" +
				"set feedback off\n" + "set define off\n";
		String suffix = ";\ncommit;\nprompt " + GeneralUtil.getNotBlankString(promptOfCompletion, "Done") + "\n";
		return prefix + sql + suffix;
	}
	
	/**
	 * 生成可直接执行的单条更新语句
	 * @param tableName 需要更新的表名
	 * @param params 各个字段名及对应字段值，与想要更新的数据库表字段精确对应，并需要额外提供一个标识字段作为匹配条件。
	 * @param keyName 标识字段名（比如id），将从params中获取对应值（生成set子句时会将其排除），更新语句再根据该值更新所有匹配行。
	 */
	public static <V> String createUpdateSql(String tableName, Map<String,V> params, String keyName) {
		V keyValue = params.remove(keyName);
		String sql = "update " + tableName + " set " + createUpdateSqlPart(params) +
				" where " + keyName + " = '" + escapeSqlParamValue(keyValue) + "'";
		params.put(keyName, keyValue);
		return sql;
	}
	
	/**
	 * 生成update语句中的set子句（不含set关键字）
	 */
	public static <V> String createUpdateSqlPart(Map<String,V> params) {
		StringBuilder sb = new StringBuilder();
		for (Map.Entry<String,V> entry : params.entrySet()) {
			sb.append(entry.getKey()).append("='").append(escapeSqlParamValue(entry.getValue())).append("',");
		}
		String sql = sb.toString().replace("''", "null");
		sql = sql.replace("='null'", "=null");
		sql = sql.substring(0, sql.length()-1);
		return sql;
	}
	
	/**
	 * 将sql中的特殊字符转义后返回新的可执行sql，暂时只支持oracle。
	 * @param sql 本方法不改变sql的任何语义或者排版格式
	 * @param dbType 传入Oracle、MySQL、SQLServer其中之一，不区分大小写。
	 */
	public static String escapeSql(String sql, String dbType) {
		if (dbType.equalsIgnoreCase("Oracle")) {
			if (sql.indexOf('&') > -1) {
				sql = sql.replace("&", "'||'&'||'");
			}
		}
		return sql;
	}
	
	/**
	 * 将查询语句嵌套在oracle的分页语句中，返回一条可执行的分页查询语句。注意，执行sql后会额外返回
	 */
	private static String wrapInLimitForOracle(String sql, int offset, int rows) {
		sql = "select * from\n"
				+ "(select t_temp_0.*, ROWNUM as ORACLE_ROWNO from "
				+ "(\n\t" + sql.replaceAll("\n", "\n\t") + "\n) t_temp_0\n"
				+ "where ROWNUM <= " + (offset + rows) + ") t_temp_1\n"
				+ "where ORACLE_ROWNO > " + offset;
		return sql;
	}
	
	/**
	 * 将查询语句嵌套在oracle的分页语句中执行，再移除结果集中多出来的ORACLE_ROWNO派生字段。
	 */
	@SuppressWarnings("unused")
	private static List<Map<String,String>> queryByLimitForOracle(String sql, int offset, int rows) {
		sql = wrapInLimitForOracle(sql, offset, rows);
		List<Map<String,String>> list = executeQuery(sql);
		ListDataUtil.removeKey(list, "ORACLE_ROWNO");
		return list;
	}
	
	public static String getDatabaseProductName(Connection conn) {
		String databaseProductName;
		try {
			databaseProductName = conn.getMetaData().getDatabaseProductName();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
		return databaseProductName.toLowerCase();
	}
	
	public static String wrapInLimit(String sql, int offset, int rows, Connection conn) {
		String databaseProductName = getDatabaseProductName(conn);
		if (databaseProductName.contains("oracle")) {
			return wrapInLimitForOracle(sql, offset, rows);
		} else if (databaseProductName.contains("mysql")) {
			return sql + "\nlimit " + offset + ", " + rows;
		} else {
			throw new RuntimeException("不支持的数据库类型：" + databaseProductName);
		}
	}
	
	public static List<Map<String,String>> queryByLimit(String sql, int offset, int rows, Connection conn) {
		sql = wrapInLimit(sql, offset, rows, conn);
		List<Map<String,String>> list = executeQuery(sql, conn);
		if (getDatabaseProductName(conn).contains("oracle")) {
			ListDataUtil.removeKey(list, "ORACLE_ROWNO");
		}
		return list;
	}
	
	/**
	 * 创建sql子句：分组并取组内第一条记录。
	 * @param tableNameWithWhereClause 表名，禁止带别名，可带where子句。
	 * @param groupBy 分组字段名
	 * @param orderBy 组内排序字段名，可携带desc描述。
	 * @return 句式：(select ROW_NUMBER() OVER(PARTITION BY ${groupBy} ORDER BY ${orderBy}) rn, t.*
	 * 				from ${tableNameWithWhereClause}) where rn = 1
	 */
	public static String createRowNumberPartitionClause(String tableNameWithWhereClause,
			String groupBy, String orderBy) {
		tableNameWithWhereClause = MicroUtil.insertTextAtFoundedPosition(
				tableNameWithWhereClause.trim(), "\\s", " t", false, true);
		String sqlPart = "\n\t(select ROW_NUMBER() OVER(PARTITION BY %s ORDER BY %s) rn, t.*\n"
				+ "\tfrom %s)\nwhere rn = 1\n";
		return String.format(sqlPart, groupBy, orderBy, tableNameWithWhereClause);
	}
	
	/**
	 * 使用列表中某个键的值拼接成in子句，带引号和括号。
	 */
	public static <K,V> String createInClauseValueStr(List<Map<K,V>> list, K key) {
		return "('" + GeneralUtil.concatString("','", ListDataUtil.getValues(list, key), true) + "')";
	}
	
	/**
	 * 获取表的元数据，返回三个子数组：字段名、类型、注释。
	 */
	public static String[][] getMetaData(String tableName) {
		Connection conn = JdbcUtil.getDbConn(null);
		try {
			DatabaseMetaData dmd = conn.getMetaData();
			if (dmd.getDatabaseProductName().toLowerCase().contains("oracle")) {
				tableName = tableName.toUpperCase();
			}
			String schema = null;
			String pureTableName = tableName;
			if (tableName.indexOf('.') > -1) {
				schema = MicroUtil.subBefore(tableName, '.');
				pureTableName = MicroUtil.subBehind(tableName, '.');
			}
			ResultSet rs = dmd.getColumns(conn.getCatalog(), schema, pureTableName, null);
			if (!rs.next()) {
				rs.close();
				throw new RuntimeException("无法获取到" + tableName + "的元数据");
			}
			String sql = "select * from " + tableName + " where 1 = 2";
			Statement stm = conn.createStatement();
			ResultSet resSet = stm.executeQuery(sql);
			ResultSetMetaData rsmd = resSet.getMetaData();
			String[] names = new String[rsmd.getColumnCount()];
			String[] types = new String[names.length];
			for (int i=0; i<names.length; i++) {
				names[i] = rsmd.getColumnLabel(i+1);
				types[i] = rsmd.getColumnClassName(i+1);
			}
			stm.close();
			String[] remarks = new String[names.length];
			for (int i=0; i<remarks.length; i++) {
				remarks[i] = rs.getString("REMARKS");
				rs.next();
			}
			rs.close();
			return new String[][] {names, types, remarks};
		} catch (SQLException e) {
			throw new RuntimeException(e);
		} finally {
			close(conn);
		}
	}
	
	@SuppressWarnings("unused")
	private static Map<String,String> getColumnRemarksFromMetaData(String[][] metaData,
			boolean truncateToBriefly) {
		Map<String,String> dictionary = new LinkedHashMap<>(metaData.length);
		for (String[] column : metaData) {
			dictionary.put(column[0], column[2]);
		}
		return dictionary;
	}
	
	/**
	 * 获取一个Connection的可读、不变的唯一标识：ip+端口，或者域名。
	 */
	public static String getUniqueIdenOfConnection(Connection conn) {
		String dbType = getDatabaseProductName(conn);
		if (dbType.contains("oracle")) {
			String url;
			try {
				url = conn.getMetaData().getURL();
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
			return MicroUtil.extractFirst(url, UnifiedConstants.REGEX_IP);
		} else {
			throw new IllegalArgumentException("暂不支持非oracle数据库");
		}
		
	}
	
}